package org.study.minio.boot.service.impl;

import com.qcloud.cos.COSClient;
import com.qcloud.cos.exception.*;
import com.qcloud.cos.model.*;
import com.qcloud.cos.utils.IOUtils;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.study.minio.boot.config.CosProperties;
import org.study.minio.boot.service.CosService;

import java.io.File;
import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * 腾讯云 COS 对象存储服务接口实现
 * 官方文档：https://cloud.tencent.com/document/product/436/10199
 *
 * @author doveylovey
 * @version v1.0.0
 * @email 1135782208@qq.com
 * @date 2024年05月18日
 */
@Slf4j
@Service
public class CosServiceImpl implements CosService {
    @Resource
    private CosProperties cosProperties;
    @Autowired(required = false)
    private COSClient cosClient;

    @Override
    public Boolean doesBucketExist(String bucketName) {
        return cosClient.doesBucketExist(bucketName);
    }

    @Override
    public Bucket createBucket(String bucketName) {
        Bucket bucket = null;
        CreateBucketRequest createBucketRequest = new CreateBucketRequest(bucketName);
        // 设置 bucket 的权限为 Private(私有读写)、PublicRead(公有读私有写)、PublicReadWrite(公有读写)
        createBucketRequest.setCannedAcl(CannedAccessControlList.Private);
        try {
            bucket = cosClient.createBucket(createBucketRequest);
        } catch (CosServiceException serverException) {
            serverException.printStackTrace();
        } catch (CosClientException clientException) {
            clientException.printStackTrace();
        }
        return bucket;
    }

    @Override
    public List<Bucket> listBuckets() {
        List<Bucket> bucketList = cosClient.listBuckets();
        for (Bucket bucket : bucketList) {
            String bucketName = bucket.getName();
            String bucketLocation = bucket.getLocation();
        }
        return bucketList;
    }

    @Override
    public PutObjectResult upload(String bucketName, String key, String localFilePath) {
        File localFile = new File(localFilePath);
        PutObjectRequest putObjectRequest = new PutObjectRequest(bucketName, key, localFile);
        PutObjectResult putObjectResult = cosClient.putObject(putObjectRequest);
        return putObjectResult;
    }

    @Override
    public ObjectListing listObjects(String bucketName, String prefix) {
        ListObjectsRequest listObjectsRequest = new ListObjectsRequest();
        listObjectsRequest.setBucketName(bucketName);
        listObjectsRequest.setPrefix(prefix);
        // delimiter 表示分隔符，设置为 / 表示列出当前目录下的 object，设置为空表示列出所有的 object
        listObjectsRequest.setDelimiter("/");
        // 设置最大遍历出多少个对象，一次 listObjects 最大支持 1000
        listObjectsRequest.setMaxKeys(1000);
        ObjectListing objectListing = null;
        do {
            try {
                objectListing = cosClient.listObjects(listObjectsRequest);
            } catch (CosServiceException e) {
                e.printStackTrace();
                return objectListing;
            } catch (CosClientException e) {
                e.printStackTrace();
                return objectListing;
            }
            // commonPrefixes 表示表示被 delimiter 截断的路径，如 delimiter 设置为 /，commonPrefixes 则表示所有子目录的路径
            List<String> commonPrefixes = objectListing.getCommonPrefixes();
            // objectSummaries 表示所有列出的 object 列表
            List<COSObjectSummary> cosObjectSummaries = objectListing.getObjectSummaries();
            for (COSObjectSummary cosObjectSummary : cosObjectSummaries) {
                // 文件的路径 key
                String key = cosObjectSummary.getKey();
                // 文件的 eTag
                String etag = cosObjectSummary.getETag();
                // 文件的长度
                long fileSize = cosObjectSummary.getSize();
                // 文件的存储类型
                String storageClasses = cosObjectSummary.getStorageClass();
            }
            String nextMarker = objectListing.getNextMarker();
            listObjectsRequest.setMarker(nextMarker);
        } while (objectListing.isTruncated());
        return objectListing;
    }

    @Override
    public String getCRC64(String bucketName, String key) {
        String crc64Ecma = null;
        // 获取下载输入流
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key);
        COSObject cosObject = cosClient.getObject(getObjectRequest);
        COSObjectInputStream cosObjectInput = null;
        try {
            cosObjectInput = cosObject.getObjectContent();
            // 下载对象的 CRC64
            crc64Ecma = cosObject.getObjectMetadata().getCrc64Ecma();
        } finally {
            if (cosObjectInput != null) {
                try {
                    // 关闭输入流
                    cosObjectInput.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
        return crc64Ecma;
    }

    @Override
    public byte[] getObjectBytes(String bucketName, String key) {
        byte[] bytes = null;
        GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key);
        COSObject cosObject = cosClient.getObject(getObjectRequest);
        // 读取流
        try (COSObjectInputStream cosObjectInput = cosObject.getObjectContent()) {
            bytes = IOUtils.toByteArray(cosObjectInput);
        } catch (CosServiceException ex) {
            ex.printStackTrace();
            if (ex.getMessage() != null && ex.getMessage().startsWith("The specified key does not exist.")) {
                throw new RuntimeException("指定文件不存在");
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        return bytes;
    }

    @Override
    public Map<String, String> downloadToLocal(String bucketName, String key, String outputFilePath) {
        Map<String, String> result = new HashMap<>(2);
        try {
            File downloadFile = new File(outputFilePath + key);
            GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key);
            ObjectMetadata downObjectMeta = cosClient.getObject(getObjectRequest, downloadFile);
            result.put("code", "0000");
            result.put("msg", "下载成功。文件保存到 " + downloadFile.getPath());
        } catch (Exception ex) {
            ex.printStackTrace();
            result.put("code", "9999");
            result.put("msg", "下载失败：" + ex.getMessage());
        }
        return result;
    }

    @Override
    public void deleteObject(String bucketName, String key) {
        cosClient.deleteObject(bucketName, key);
    }

    @Override
    public List<String> deleteObjects(String bucketName, List<String> keys) {
        List<String> successList = new ArrayList<>();
        DeleteObjectsRequest deleteObjectsRequest = new DeleteObjectsRequest(bucketName);
        // 设置要删除的key列表, 最多一次删除1000个
        ArrayList<DeleteObjectsRequest.KeyVersion> keyList = new ArrayList<>();
        // 传入要删除的文件名
        keys.parallelStream().forEach(item -> {
            keyList.add(new DeleteObjectsRequest.KeyVersion(item));
        });
        deleteObjectsRequest.setKeys(keyList);
        try {
            // 批量删除文件
            DeleteObjectsResult deleteObjectsResult = cosClient.deleteObjects(deleteObjectsRequest);
            List<DeleteObjectsResult.DeletedObject> deleteObjectResultArray = deleteObjectsResult.getDeletedObjects();
            if (!CollectionUtils.isEmpty(deleteObjectResultArray)) {
                successList = deleteObjectResultArray.parallelStream()
                        .map(item -> item.getKey())
                        .collect(Collectors.toList());
            }
        } catch (MultiObjectDeleteException mde) {
            // 如果部分删除成功部分失败，返回 MultiObjectDeleteException
            List<DeleteObjectsResult.DeletedObject> deleteObjects = mde.getDeletedObjects();
            successList = deleteObjects.parallelStream()
                    .map(item -> item.getKey())
                    .collect(Collectors.toList());
            List<MultiObjectDeleteException.DeleteError> deleteErrors = mde.getErrors();
            List<String> errorList = deleteErrors.parallelStream()
                    .map(item -> item.getKey())
                    .collect(Collectors.toList());
            log.error("删除失败：" + errorList);
        } catch (CosServiceException e) {
            // 如果是其他错误，例如参数错误，身份验证不过等会抛出 CosServiceException
            e.printStackTrace();
        } catch (CosClientException e) {
            // 如果是客户端错误，例如连接不上COS
            e.printStackTrace();
        }
        return successList;
    }
}
